﻿using FreeSql;
using FreeSql.Aop;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Singer.Core;
using Singer.Shared;
using Singer.Shared.Domain;
using Singer.Shared.Modularity;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;

namespace Singer.Middleware.FreeSql
{
    public static class FreeSqlServiceExtensions
    {
        public static void AddFreeSql(this IServiceCollection services, IConfiguration configuration, Action<FreeSqlBuilder>? builderAction = null)
        {
            FreeSqlOptions? options = configuration.GetSection(FreeSqlOptions.ConfigKey).Get<FreeSqlOptions>();
            if (options == null)
                throw new Exception("【FreeSql】Config was not found.");
            options.Validate();
            FreeSqlBuilder freeSqlBuilder = new FreeSqlBuilder()
                .UseConnectionString(options.DataType, options.ConnectionString)
                .UseAutoSyncStructure(options.AutoSyncStructure)//自动同步表结构。开发环境很快乐，生产环境千万别用！
                .UseMonitorCommand(x => Console.WriteLine("【FreeSql】：" + x.CommandText));
            builderAction?.Invoke(freeSqlBuilder);
            IFreeSql freeSql = freeSqlBuilder.Build();
            freeSql.UseJsonMap();
            //全局过滤器，过滤掉软删除的数据
            freeSql.GlobalFilter.ApplyOnly<ISoftDelete>(Consts.SoftDeleteFilterName, x => x.IsDeleted == false);
            services.AddSingleton<IFreeSql>(sp =>
            {
                IUserContextAccessor? userContextAccessor = sp.GetService<IUserContextAccessor>();
                if (options.UseAopAudit)
                    freeSql.Aop.AuditValue += (sender, e) => Aop_AuditValue(sender, e, userContextAccessor);//Aop新增编辑触发事件
                return freeSql;
            });
        }

        /// <summary>
        /// 继承了ICreator的实体在新增数据时自动加上创建人信息
        /// 继承了IChanger的实体在修改数据时自动加上修改人信息 （必须先查再修改，内存中有实体信息的才可以）
        /// </summary>
        private static void Aop_AuditValue(object? sender, AuditValueEventArgs e, IUserContextAccessor? userContextAccessor)
        {
            e.ObjectAuditBreak = true;
            IUserContext? _userContext = userContextAccessor?.UserContext;
            if (e.AuditValueType == AuditValueType.Insert)
            {
                if (e.Object is ICreator creator)
                {
                    if (string.IsNullOrWhiteSpace(creator.CreatorId) && !string.IsNullOrWhiteSpace(_userContext?.UserId))
                        e.Object.GetType().GetProperty(nameof(creator.CreatorId))?.SetValue(e.Object, _userContext.UserId);
                    if (creator.CreateTime == DateTime.MinValue)
                        e.Object.GetType().GetProperty(nameof(creator.CreateTime))?.SetValue(e.Object, Cores.BeiJingNow);
                }
            }
            else if (e.AuditValueType == AuditValueType.Update)
            {
                if (e.Object is IChanger changer)
                {
                    if (!string.IsNullOrWhiteSpace(_userContext?.UserId))
                        e.Object.GetType().GetProperty(nameof(changer.ChangerId))?.SetValue(e.Object, _userContext.UserId);
                    e.Object.GetType().GetProperty(nameof(changer.ChangeTime))?.SetValue(e.Object, Cores.BeiJingNow);
                }
            }
        }
    }
}
